// ...........................................................................................................
// (C) Copyright  1996/2008 Fuego Inc.  All Rights Reserved
// THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF Fuego Inc.
// The copyright notice above does not evidence any actual or intended
// publication of such source code.
//
// Last changed on 2008-01-31 14:19:06 (-0300), by: pcolunga. $Revision$
// ...........................................................................................................

package com.webcamtracker.tracker.example.spatial;

import com.sun.j3d.utils.geometry.Cylinder;
import com.webcamtracker.move.tracking.TrackingEvent;
import com.webcamtracker.move.tracking.TrackingEventListener;

import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Material;
import javax.media.j3d.PointArray;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

public class Spatial
        extends TransformGroup
        implements TrackingEventListener {

    /**
     * Point Location > It varies:
     * on the "x" axis between (-pointCanvasSize.getWidth/2  : +pointCanvasSize.getWidth/2)
     * on the "y" axis between (-pointCanvasSize.getHeight/2 : +pointCanvasSize.getHeight/2)
     */
    private Point pointLocation = new Point();

    private final TransformGroup space;

    private final int canvasWidth;
    private final int canvasHeight;

    private float zoom;
    public static final int PORTAL_WIDTH = 700;
    public static final int PORTAL_HEIGHT = 700;
    private TrackTransformation trackTransformation;

    //~ Constructors .........................................................................................

    public Spatial(TransformGroup space, int canvasWidth, int canvasHeight) {
        this.space = space;
        this.canvasWidth = canvasWidth;
        this.canvasHeight = canvasHeight;

        this.pointLocation = new Point(this.canvasWidth / 2, this.canvasHeight / 2);
        this.zoom = 1;

        TransformGroup spatial = new TransformGroup();

        spatial.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        spatial.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

        for (Stick stick : getSticks()) {
            spatial.addChild(stick);
        }

        addChild(background()); // Remove this or extend bases for each component....
        addChild(spatial);

        initTrackingRotation();

        light();
    }

    //~ Methods ..............................................................................................

    private void initTrackingRotation() {
        this.trackTransformation = new TrackTransformation(this.space);
        this.space.addChild(this.trackTransformation);
        this.trackTransformation.setBounds(new BoundingSphere(new Point3d(0.0, 0.0, -4.0), 100.0));
    }

    private List<Stick> getSticks() {
        List<Stick> sticks = new ArrayList<Stick>();

        float spaceFactor = 1.5F;

        sticks.add(new Stick(.0F * spaceFactor, .0F * spaceFactor, 2.0F));
        sticks.add(new Stick(.5F * spaceFactor, .9F * spaceFactor, 2.5F));
        sticks.add(new Stick(.35F * spaceFactor, .55F * spaceFactor, 1.0F)); //
        sticks.add(new Stick(-.95F * spaceFactor, -.35F * spaceFactor, 2.8F));
        sticks.add(new Stick(-.35f * spaceFactor, .35f * spaceFactor, 2.2F)); //
        sticks.add(new Stick(.35f * spaceFactor, -.35f * spaceFactor, 1.15F)); //
        sticks.add(new Stick(-.55f * spaceFactor, -.25f * spaceFactor, 1.25F)); //
        sticks.add(new Stick(.75f * spaceFactor, -.85f * spaceFactor, 1.75F));
        sticks.add(new Stick(.95f * spaceFactor, .15f * spaceFactor, 2.75F)); //
        sticks.add(new Stick(.45f * spaceFactor, -.65f * spaceFactor, 1.5F));

        return sticks;
    }

    private void light() {
        Color3f lightColor = new Color3f(1.8f, 1.8f, 1.8f);

        BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

        Vector3f light1Direction = new Vector3f(0.4f, -7.0f, -12.0f);
        DirectionalLight light1 = new DirectionalLight(lightColor, light1Direction);
        light1.setInfluencingBounds(bounds);
        addChild(light1);

        AmbientLight ambientLight = new AmbientLight(new Color3f(1.4f, 1.4f, 1.4f));
        ambientLight.setInfluencingBounds(bounds);
        addChild(ambientLight);
    }

    private TransformGroup background() {

        Material m = new Material();
//        m.setDiffuseColor(0.35f, 0.2f, 0.0f);
        m.setDiffuseColor(new Color3f(Color.LIGHT_GRAY));
        Appearance a = new Appearance();
        a.setMaterial(m);

        //render the Box as a wire frame
        PolygonAttributes polyAttrbutes = new PolygonAttributes();
        polyAttrbutes.setPolygonMode(PolygonAttributes.POLYGON_LINE);
        polyAttrbutes.setCullFace(PolygonAttributes.CULL_NONE);
        a.setPolygonAttributes(polyAttrbutes);

        TransformGroup tg = new TransformGroup();

        float initDepth = -4F;
        float deltaDepth = -2.5F;

        for (int i = 0; i < 12; i++) {
            tg.addChild(buildBox(a, initDepth + deltaDepth * i));
        }

        return tg;
    }

    private TransformGroup buildBox(Appearance a, float d) {
        TransformGroup boxTg = new TransformGroup();
        Transform3D transform = new Transform3D();
        Vector3f vector = new Vector3f(.0f, .0f, d);
        transform.setTranslation(vector);
        boxTg.setTransform(transform);
        boxTg.addChild(boxEdge(0, 4, true));
        boxTg.addChild(boxEdge(0, -4, true));
        boxTg.addChild(boxEdge(4, 0, false));
        boxTg.addChild(boxEdge(-4, 0, false));
        return boxTg;
    }

    private TransformGroup boxEdge(int x, int y, boolean rotate) {
        Appearance a = new Appearance();
        PolygonAttributes polyAttrbutes = new PolygonAttributes();
        polyAttrbutes.setPolygonMode(PolygonAttributes.POLYGON_LINE);
//        polyAttrbutes.setCullFace(PolygonAttributes.CULL_NONE);
        a.setPolygonAttributes(polyAttrbutes);
        Cylinder top = new Cylinder(.01F, 8F, 1, 2, 1, a);
        TransformGroup topTg = new TransformGroup();
        Transform3D transform = new Transform3D();
        Vector3f vector = new Vector3f(x, y, .0f);
        if (rotate) transform.rotZ(Math.PI / 2);
        transform.setTranslation(vector);
        topTg.setTransform(transform);
        topTg.addChild(top);
        return topTg;
    }

    private void something() {
        int nGridSize = 8;

        PointArray m_PointArray = new PointArray(nGridSize *
                nGridSize, GeometryArray.COORDINATES);
        Transform3D m_t3d = new Transform3D();
        float m_rot = 0.0f;

        //create the PointArray that we will be rendering
        int nPoint = 0;
        for (int n = 0; n < nGridSize; n++) {
            for (int i = 0; i < nGridSize; i++) {
                Point3f point = new Point3f(n - nGridSize / 2,
                        i - nGridSize / 2, 0.0f);

                m_PointArray.setCoordinate(nPoint++, point);
            }
        }


    }

    private void setPointLocation(Point point) {
        this.pointLocation.x = canvasWidth / 2 - (canvasWidth - point.x);
        this.pointLocation.y = canvasHeight / 2 - (canvasHeight - point.y);
    }

    public void trackingEventDispatched(TrackingEvent event) {
        //Set the new center point
        setPointLocation(event.getPoint());

        this.zoom = event.getZoomFactor();

        executeTranslation();
    }

    private void executeTranslation() {
        this.trackTransformation.doProcess(
                (int) getNextTranslationFromX(),
                (int) getNextTranslationFromY(),
                this.zoom);
    }

    private float getNextTranslationFromY() {
        return (float) this.pointLocation.y / 5;
    }

    private float getNextTranslationFromX() {
        return (float) this.pointLocation.x / 5;
    }
}
